/**
 * ****************************************************************************
 * Project: RoboBridge
 * ----------------------------------------------------------------------------
 * -------------------------- [c]2012 - KrawallMann ---------------------------
 * ---------------------- http://www.krawallmann.de.tc/ -----------------------
 * ****************************************************************************
 * File: WLANDeviceScanner.java Package:
 * tc.de.krawallmann.robobridge.communication.lan Version: 1.0 Author(s):
 * KrawallMann
 * ****************************************************************************
 * Description:
 * ****************************************************************************
 */
package tc.de.krawallmann.robobridge.communication.lan.wlan;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.logging.Level;

import tc.de.krawallmann.robobridge.communication.AbstractDeviceScanner;
import tc.de.krawallmann.robobridge.util.Log;

/**
 * The Class WLANDeviceScanner scans the WLAN for RP6 robots.
 * 
 * @author KrawallMann
 * @version 1.0
 */
public class WLANDeviceScanner extends AbstractDeviceScanner<WLANRemoteDevice> implements Runnable {

	/** The broadcast ip. */
	private transient final String broadcastIp;

	/** The broadcast port. */
	private transient final int broadcastPort;

	/** The do run. */
	private transient boolean doRun;

	/** The socket. */
	private transient DatagramSocket socket;

	/**
	 * Instantiates a new lAN device scanner.
	 */
	@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
	public WLANDeviceScanner() {
		super();
		broadcastIp = "0.0.0.0";
		broadcastPort = 55555;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.AbstractDeviceScanner#close()
	 */
	@Override
	public void close() {
		doRun = false;
		socket.close();
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.AbstractDeviceScanner#isScanning
	 * ()
	 */
	@Override
	public boolean isScanning() {
		return socket != null && socket.isConnected();
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.AbstractDeviceScanner#open()
	 */
	@Override
	public void open() throws IOException {
		if (socket != null) {
			throw new IllegalStateException("Already open!");
		}
		socket = new DatagramSocket(broadcastPort, InetAddress.getByName(broadcastIp));
		socket.setBroadcast(true);
		final Thread thread = new Thread(this);
		thread.setName("DeviceScanner");
		doRun = true;
		thread.start();

	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		final byte[] buf = new byte[1024];
		final DatagramPacket packet = new DatagramPacket(buf, buf.length);
		while (doRun) {
			try {
				socket.receive(packet);
				final String str = new String(packet.getData());
				if (str.contains("WiFly")) {
					// That's the WIFI module's name!
					final byte[] deviceId = new byte[32];
					if (packet.getData().length < 92) {
						// Not a RP6
						continue;
					}
					System.arraycopy(packet.getData(), 60, deviceId, 0, 32);
					final WLANRemoteDevice foundDevice =
							new WLANRemoteDevice(packet.getAddress().getHostAddress(),
									packet.getPort(), new String(deviceId).trim());
					final int rssi = -packet.getData()[7];
					if (containsCommunicationDevice(foundDevice)) {
						final WLANRemoteDevice dev =
								getCommunicationDevice(foundDevice.getAddress());
						dev.setRssi(rssi);
					} else {
						foundDevice.setRssi(rssi);
						addCommunicationDevice(foundDevice);
					}
				} else {
					Log.log(Level.INFO,
							getClass(),
							"run",
							"Received Broadcast from device (Probably not RP6) at {0}:{1} - ignoring",
							packet.getAddress().getHostAddress(), packet.getPort());
				}
			} catch (final IOException e) {
				doRun = false;
				Log.log(Level.WARNING, getClass(), "run", "Exception while scanning - {0}",
						e.getMessage());
			}
		}
	}

}
